#pragma once
#ifndef PROCESSPOOL_HPP_H
#define PROCESSPOOL_HPP_H

#include <vector>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include "Task.hpp"

class Channel
{
public:
    Channel(int wfd, int pid)
        : _wfd(wfd), _pid(pid)
    {
    }
    ~Channel()
    {
    }
    int Wfd() { return _wfd; }
    int Pid() { return _pid; }

private:
    int _wfd; // 控制子进程
    int _pid; // 拿到子进程pid，方便回收
};

// debug
void work(int rfd)
{
    while (true)
    {
        sleep(1);
        std::cout << "子进程pid:" << getpid() << " 子进程rfd:" << rfd << std::endl;
    }
}

class ChannelManager
{
public:
    ChannelManager(int num, const TaskManager &tm) : _next(0)
    {
        for (int i = 0; i < num; i++)
        {
            // 1.创建管道
            int pipefd[2] = {0};
            int ret = pipe(pipefd);
            if (ret != 0)
                exit(1);
            // 2.创建子进程
            pid_t pid = fork();
            // 3.关闭父读，子写
            if (pid < 0)
                exit(1);
            else if (pid == 0)
            {
                // 子进程关闭写
                close(pipefd[1]);
                //std::cout << "子进程的读pipefd"<<pipefd[1] << std::endl;
                // 子进程工作
                printf("ChildRead开始，进程为%d\n",getpid());
                ChildRead(pipefd[0], tm);
                //work(pipefd[0]);
                close(pipefd[0]);
                // 子进程完成工作，退出
                exit(0);
            }
            // 父进程关闭读
            close(pipefd[0]);
            // emplace_back直接构造，插入到_channels
            _channels.emplace_back(pipefd[1], pid);
            // 循环num次
        }
    }

    // 为了保证负载均衡，采用轮询的方式
    Channel &SelectProcess()
    {
        // 选出下标
        ++_next;
        _next %= _channels.size();
        return _channels[_next];
    }

    void ChildRead(int rfd, const TaskManager &tm)
    {
        int taskcode = 0;
        while (true)
        {
            ssize_t ret = read(rfd, &taskcode, sizeof(taskcode));
            // 父进程写端关闭了，子进程要结束
            if (ret == 0)
            {
                std::cout << "父进程写端关闭，子进程:" << getpid() << "退出" << std::endl;
                break;
            }
            // 读到的不是4字节，丢弃，重新读
            if (ret != sizeof(taskcode))
            {
                printf("丢弃\n");
                return;
            }
            // 执行相应任务
            printf("进程：%d ExcuteTask开始，ret:%d，taskcode:%d\n",getpid(),(int)ret,taskcode);
            tm.ExecuteTask(taskcode);
        }
    }

    void CloseChannels()
    {
        for(size_t i = 0;i<_channels.size();i++)
        {
            //关闭父进程的写fd，让子进程读到0个字节退出。
            close(_channels[i].Wfd());
            printf("关闭了父的写fd %d\n",_channels[i].Wfd());
        }
        //回收子进程的僵尸状态
        for(size_t i = 0;i<_channels.size();i++)
        { 
            printf("等待开始，要回收子进程：%d\n",_channels[i].Pid());
            waitpid(_channels[i].Pid(),nullptr,0);
            printf("等待成功，回收了子进程：%d\n",_channels[i].Pid());
        }
    }

    ~ChannelManager()
    {

    }

private:
    std::vector<Channel> _channels;
    int _next;
};

class ProcessPool
{
public:
    ProcessPool(int num)
        :_tm(),_cm(num, _tm)
    {}
    ~ProcessPool()
    {

    }

    // 选择一个子进程，随机发送任务
    void Run()
    {
        // 1.选择一个子进程
        Channel &c = _cm.SelectProcess();
        printf("挑选的子进程为：%d\n",c.Pid());
        // 2.获取任务码
        int taskcode = _tm.TaskCode();

        /*printf("父进程：%d 执行任务，taskcode：%d\n",getpid(),taskcode);
        _tm.ExecuteTask(taskcode);*/

        // 3.发送任务码给子进程，子进程执行(写给子进程)
        printf("父进程：%d 写入taskcode：%d\n",getpid(),taskcode);
        ssize_t ret = write(c.Wfd(), &taskcode, sizeof(taskcode));
    }

    void Close()
    {
        _cm.CloseChannels();
    }

private:
    TaskManager _tm;
    ChannelManager _cm;
};

#endif